/*
 * commands.c
 *
 *  Created on: 8 Nov 2017
 *      Author: Standa
 */

#include "commands.h"
#include "info.h"
#include "com.h"
#include "error.h"
#include "utility.h"
#include "stdio.h"
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include "crc.h"
#include "can.h"


#define HELP_LINE_1		"> CANMaster help.\r\n"
#define HELP_LINE_2		"> Space or tab interchangeably used as separator. Below in [] brackets is variable.\r\n"
#define HELP_LINE_3		"> Generic command syntax is: [CRC] [Cmd] [CmdId] [Param1] [Param2] ... [ParamN].\r\n"
#define HELP_LINE_4		"> Each command is acknowledged immediately: [CRC] ack [Cmd] [CmdId] [ErrorCode] [TimeStamp_ms]\r\n"
#define HELP_LINE_5		"> Events are asynchronous to command reception and acknowledge. CRC parameter is optional.\r\n"
#define HELP_LINE_6		"> Transmit message: [CRC] tx [CmdId] [MsgId] [ByteCnt] [B0] [B1] [B2] [B3] [B4] [B5] [B6] [B7].\r\n"
#define HELP_LINE_7		"> Answer is event: [CRC] evt canrx [TimeStamp_ms] [MsgId] [ByteCnt] [B0] [B1] [B2] [B3] [B4] [B5] [B6] [B7].\r\n"
#define HELP_LINE_8		"> Byte order might be changed for both tx command and canrx event: [CRC] so [CmdId] [Order 0=normal/1=inv].\r\n"
#define HELP_LINE_9		"> Currently set byte order might be read: [CRC] go [CmdId]. Response: [CRC] go [CmdId] [Order].\r\n"
#define HELP_LINE_10	"> Single next CAN transmission might be interrupted at specified delay time for specified duration\r\n"
#define HELP_LINE_11	"> using command: [CRC] si [CmdId] [Delay_us_uint16] [Duration_us_uint16].\r\n"
#define HELP_LINE_12	"> Please note that there is inherent 2us delay which adds to specified Delay_us_uint16.\r\n"
#define HELP_LINE_13	"> Enjoy the tool.\r\n"


typedef struct{
	char* Name;
	err_Td (*CallbackFn)(char *cmdName, int32_t cmdId);
}CmdTd;


/*
 * Get information about the instrument.
 * Syntax: [CRC] [CmdName] [CmdId]\r\n
 * Response: [CRC] cmd [CmdName] [CmdId] [Company] [Author] [Device] [HWVer] [FWVer] [Id] [CalDate]\r\n
 */
static err_Td GetInfoCb(char *cmdName, int32_t cmdId){
	com_SendF("0 cmd %s %d %s %s %s %s %s %s %s\r\n", cmdName, cmdId, INFO_COMPANY, INFO_AUTHOR, INFO_DEVICE, INFO_HWVER, INFO_FWVER, INFO_ID, INFO_CALDATE);
	return err_Td_Ok;
}


/*
 * Get help file.
 * Syntax: [CRC] [CmdName] [CmdId]\r\n
 * Response: series of separate lines will deliver the content of help (standard ack will indicate end).
 */
static err_Td GetHelpCb(char *cmdName, int32_t cmdId){
	com_SendF(HELP_LINE_1);
	com_SendF(HELP_LINE_2);
	com_SendF(HELP_LINE_3);
	com_SendF(HELP_LINE_4);
	com_SendF(HELP_LINE_5);
	com_SendF(HELP_LINE_6);
	com_SendF(HELP_LINE_7);
	com_SendF(HELP_LINE_8);
	com_SendF(HELP_LINE_9);
	com_SendF(HELP_LINE_10);
	com_SendF(HELP_LINE_11);
	com_SendF(HELP_LINE_12);
	com_SendF(HELP_LINE_13);
	return err_Td_NotImple;
}


/*
 * Send the message via CAN interface. All the parameters from MsgId forward are in hexadecimal form:
 * An optional prefix indicating octal or hexadecimal base ("0" or "0x"/"0X" respectively)
 * Syntax B0 to B7 might be inverted using SetByteOrder command to B7 to B0.
 * Syntax: [CRC] [CmdName] [CmdId] [MsgId] [ByteCnt] [B0] [B1] [B2] [B3] [B4] [B5] [B6] [B7]\r\n
 * Response: none
 */
static err_Td TransmitCb(char *cmdName, int32_t cmdId){
	uint32_t i, Id, ByteCnt, TxMailbox;
	uint8_t Data[8];
	CAN_TxHeaderTypeDef TxHeader = {0};

	Id = strtol(strtok(0, " \r"), 0, 16);
	ByteCnt = strtol(strtok(0, " \r"), 0, 16);
	for( i=0; i<UT_SIZEOFARRAY(Data); i++){

		if(com_GetByteOrder()){						//If inverted syntax is requested
			Data[UT_SIZEOFARRAY(Data)-1-i] = strtol(strtok(0, " \r"), 0, 16);
		}
		else{										//Else if normal syntax is requested
			Data[i] = strtol(strtok(0, " \r"), 0, 16);
		}

	}


	TxHeader.StdId = Id;
	TxHeader.ExtId = 0x00;
	TxHeader.RTR = CAN_RTR_DATA;
	TxHeader.IDE = CAN_ID_STD;
	TxHeader.DLC = ByteCnt;
	TxHeader.TransmitGlobalTime = DISABLE;

	if(HAL_CAN_AddTxMessage(&hcan, &TxHeader, Data, &TxMailbox) == HAL_OK) return err_Td_Ok;
	else return err_Td_General;
}


/*
 * Sets CAN baud rate. Possible values: 250, 500, 1000 kbps.
 * Syntax: [CRC] [CmdName] [CmdId] [BR_kbps]\r\n
 * Response: none
 */
static err_Td SetBaudRateCb(char *cmdName, int32_t cmdId){
	uint32_t BR;
	BR = strtol(strtok(0, " \r"), 0, 10);
	return com_SetCANSpeed(BR);
}


/*
 * Get CAN baud rate. Possible values: 250, 500, 1000 kbps or 0 in case of initialization failure (invalid parameter etc).
 * Syntax: [CRC] [CmdName] [CmdId] [BR_kbps]\r\n
 * Response: [CRC] cmd [CmdName] [CmdId] [BR_kbps]\r\n
 */
static err_Td GetBaudRateCb(char *cmdName, int32_t cmdId){
	com_SendF("0 cmd %s %d %d\r\n", cmdName, cmdId, com_GetBaudrate());
	return err_Td_Ok;
}

/*
 * Interrupt the can transmission after specified time after first CAN_TX first change (edge detection)
 * Syntax: [CRC] [CmdName] [CmdId] [Delay_us_uint16] [Duration_us_uint16]\r\n
 * Response: none
 */
static err_Td SetInterruptionCb(char *cmdName, int32_t cmdId){
	uint32_t Delay_us, Duration_us;
	Delay_us = strtol(strtok(0, " \r"), 0, 10);
	Duration_us = strtol(strtok(0, " \r"), 0, 10);

	return com_SetTxInterruption(Delay_us, Duration_us);
}


/*
 * Sets byte order as defined by Order parameter: 0 = normal order from B0 to B7; 1 = inverted from B7 to B0
 * Syntax: [CRC] [CmdName] [CmdId] [Order]\r\n
 * Response: none
 */
static err_Td SetByteOrderCb(char *cmdName, int32_t cmdId){
	uint32_t Order;
	Order = strtol(strtok(0, " \r"), 0, 16);

	com_SetByteOrder((uint8_t)Order);
	return err_Td_Ok;
}


/*
 * Get byte order. Order: 0 = normal order from B0 to B7; 1 = inverted from B7 to B0.
 * Syntax: [CRC] [CmdName] [CmdId]\r\n
 * Response: [CRC] cmd [CmdName] [CmdId] [Order]\r\n
 */
static err_Td GetByteOrderCb(char *cmdName, int32_t cmdId){
	com_SendF("0 cmd %s %d %d\r\n", cmdName, cmdId, com_GetByteOrder());
	return err_Td_Ok;
}


/*
 * List of all available commands. Syntax is specified in each callback function separately (also in toltip).
 */
static CmdTd CmdList[] = {
	{"gi", GetInfoCb},
	{"gh", GetHelpCb},
	{"tx", TransmitCb},
	{"si", SetInterruptionCb},
	{"sr", SetBaudRateCb},
	{"gr", GetBaudRateCb},
	{"so", SetByteOrderCb},
	{"go", GetByteOrderCb},
};


/*	Command parsing core, expects commands in following order:
 *	CRC Name Id Param1 Param2 ... ParamN\r\n
 */
void cmd_Handle(char *str){
	char *Name, AckStr[64];
	uint32_t i = 0, RecCRC = 0, CalCRC = 0, Id = 0, ErrNo = err_Td_Ok, IsNumber;

	Name = strtok(str, " \r");																		//Read name from beginning of command (or it could be CRC if it is number)
	RecCRC = atoi(Name);																			//Try to convert cmd name to number

	IsNumber = 1;																					//Here we need to check whether returned zero is because CRC is zero or because CRC is not used and we tried to convert cmd name to number
	for( i=0; i<strlen(Name); i++ ){																//Check whole command name (or it could be CRC)
		if( Name[i] < '0' || Name[i] > '9' ){														//If any letter does not contain number
			IsNumber = 0;																			//Set flag that we have command name and not CRC number
			break;																					//No need to continue check
		}
	}

	if( IsNumber ){																					//If IsNumber flag is set we have found RecCRC and name will follow in next token.
		i = strlen(Name);																			//Length of CRC
		CalCRC = HAL_CRC_Calculate(&hcrc, (uint32_t*)(Name + i + 1), strlen(Name + i + 1));			//Calculate CRC starting with CmdName and ending with \n
		Name = strtok(0, " \r");																	//Get command name
		if( RecCRC != CalCRC){																		//Compare received and calculated CRCs
			ErrNo = err_Td_CRC;																		//If it doesnt match return error
		}
	}

	Id = atoi(strtok(0, " \r"));																	//Get command ID

	if( ErrNo == err_Td_Ok ){																		//If there is no error in CRC, parse rest of command
		ErrNo = err_Td_NotExist;
		for( i=0; i<UT_SIZEOFARRAY(CmdList); i++ ){													//Repeat through whole list of defined commands
			if( strcmp(CmdList[i].Name, Name ) == 0 ){												//If command name from defined list is equal to current command name
				if( CmdList[i].CallbackFn != 0 ){
					ErrNo = CmdList[i].CallbackFn(Name, Id);										//Call callback function to parse remaining data
				}
				break;																				//Break lookup cycle if command has been found
			}
		}
	}

	i = snprintf(AckStr, sizeof(AckStr), "ack %s %u %u %u\r\n", Name, (unsigned)Id, (unsigned)ErrNo, (unsigned)HAL_GetTick());	//Send ack with errorcode
	CalCRC = HAL_CRC_Calculate(&hcrc, (uint32_t*)(AckStr), i);
	com_SendF("%08x %s", CalCRC, AckStr);
}
